﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;

namespace JasLib.Wpf
{
    /*****************************************************************************/
    public class EasyBindCommand : ViewModelBase<EasyBindCommand>, ICommand
    {
        #region ICommand implementation

        private event EventHandler _canExecuteChanged;
        event EventHandler ICommand.CanExecuteChanged
        {
            add { _canExecuteChanged += value; }
            remove { _canExecuteChanged -= value; }
        }

        protected virtual void OnCanExecuteChanged()
        {
            if (_canExecuteChanged != null)
                _canExecuteChanged(this, EventArgs.Empty);
        }

        public void Execute(object objParameter = null)
        {
            VerifyAccess();
            if (!CanExecute)
                throw new InvalidOperationException();
            ExecuteCallback(objParameter);
            return;
        }

        bool ICommand.CanExecute(object objParameter)
        {
            return CanExecute;
        }

        #endregion

        #region Dependency properties

        public static readonly DependencyProperty CanExecuteProperty = DependencyProperty.Register(
            "CanExecute",
            typeof(bool),
            typeof(EasyBindCommand),
            new FrameworkPropertyMetadata(
                false,
                FrameworkPropertyMetadataOptions.Inherits,
                CanExecuteProperty_Changed,
                CanExecuteProperty_Coerce));

        public bool CanExecute
        {
            get { return (bool)GetValue(CanExecuteProperty); }
            set { SetValue(CanExecuteProperty, value); }
        }

        private static void CanExecuteProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as EasyBindCommand).OnCanExecuteChanged();
            return;
        }

        private static object CanExecuteProperty_Coerce(DependencyObject d, object baseValue)
        {
            Func<object, bool> func = (d as EasyBindCommand).CanExecuteCallback;
            if (func != null)
            {
                bool bValue = func(null);
                return bValue;
            }
            return baseValue;
        }

        private static Func<object, bool> s_defaultCanExecuteCallback = new Func<object, bool>(obj => false);

        public static readonly DependencyProperty CanExecuteCallbackProperty = DependencyProperty.Register(
            "CanExecuteCallback",
            typeof(Func<object, bool>),
            typeof(EasyBindCommand),
            new FrameworkPropertyMetadata(
                null,
                FrameworkPropertyMetadataOptions.Inherits,
                CanExecuteCallbackProperty_Changed));

        /// <summary>
        /// If this value is set, then the CanExecute dependency property will be ignored.
        /// </summary>
        public Func<object, bool> CanExecuteCallback
        {
            get { return (Func<object, bool>)GetValue(CanExecuteCallbackProperty); }
            set { SetValue(CanExecuteCallbackProperty, value); }
        }

        private static void CanExecuteCallbackProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as EasyBindCommand).CoerceValue(CanExecuteProperty);
            return;
        }

        private static Action<object> s_defaultExecuteCallback = new Action<object>(obj => { });

        public static readonly DependencyProperty ExecuteCallbackProperty = DependencyProperty.Register(
            "ExecuteCallback",
            typeof(Action<object>),
            typeof(EasyBindCommand),
            new FrameworkPropertyMetadata(
                s_defaultExecuteCallback,
                FrameworkPropertyMetadataOptions.Inherits,
                ExecuteProperty_Changed,
                ExecuteProperty_Coerce));

        public Action<object> ExecuteCallback
        {
            get { return (Action<object>)GetValue(ExecuteCallbackProperty); }
            set { SetValue(ExecuteCallbackProperty, value); }
        }

        private static void ExecuteProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as EasyBindCommand).CoerceValue(CanExecuteProperty);
            return;
        }

        private static object ExecuteProperty_Coerce(DependencyObject d, object baseValue)
        {
            return baseValue ?? s_defaultExecuteCallback;
        }

        #endregion

        public EasyBindCommand(Action<object> executeAction = null, Func<object, bool> canExecuteFunc = null)
        {
            /// If nothing is specified, leave these unset and let coercion handle it.
            if (executeAction != null)
                this.ExecuteCallback = executeAction;
            if (canExecuteFunc != null)
                this.CanExecuteCallback = canExecuteFunc;
            return;
        }
    }
}
